package cn.wizzer.app.user.modules.service.impl;

import cn.wizzer.app.user.modules.models.GroupInfo;
import cn.wizzer.app.user.modules.models.GroupLog;
import cn.wizzer.app.user.modules.models.GroupMember;
import cn.wizzer.app.user.modules.models.User;
import cn.wizzer.app.user.modules.models.constant.ChatConstant;
import cn.wizzer.app.user.modules.models.enums.MsgTypeEnum;
import cn.wizzer.app.user.modules.models.enums.OprTypeEnum;
import cn.wizzer.app.user.modules.models.vo.GroupMsgVO;
import cn.wizzer.app.user.modules.models.vo.GroupVO;
import cn.wizzer.app.user.modules.services.*;
import cn.wizzer.framework.base.model.enums.BizExceptionEnum;
import cn.wizzer.framework.base.service.BaseServiceImpl;
import cn.wizzer.framework.base.service.CommonDBService;
import cn.wizzer.framework.page.Pagination;
import cn.wizzer.framework.util.Result;
import com.alibaba.dubbo.config.annotation.Service;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;

import java.util.Comparator;
import java.util.List;
import java.util.Random;

@IocBean(args = {"refer:dao"})
@Service(interfaceClass = GroupInfoService.class)
public class GroupInfoServiceImpl extends BaseServiceImpl<GroupInfo> implements GroupInfoService {
    public GroupInfoServiceImpl(Dao dao) {
        super(dao);
    }

    @Inject
    private CommonDBService dbService;
    @Inject
    private GroupMemberService groupMemberService;
    @Inject
    private GroupLogService groupLogService;
    @Inject
    private UserService userService;
    @Inject
    private RedisService redisService;
    @Inject
    private GroupApplyService groupApplyService;

    @Override
    public Pagination list(String userId, int pageNumber, int pageSize) {
        Sql sql = Sqls.create("SELECT i.*" +
                " FROM group_member m " +
                " LEFT JOIN group_info i ON i.id = m.groupId " +
                " WHERE m.userId = @userId " +
                " AND m.delFlag = 0 AND i.delFlag = 0 ORDER BY i.lastCommunicationTime DESC ");
        sql.setParam("userId", userId);
        Pagination pagination = dbService.listPage(GroupMsgVO.class, pageNumber, pageSize, sql.toString());
        List<GroupMsgVO> list = pagination.getList(GroupMsgVO.class);
        User fetchUser = userService.fetch(userId);
        for (GroupMsgVO groupMsgVO : list) {
            // 如果用户状态为isSessionDisabled，私聊以外的群都禁言
            if (fetchUser.isSessionDisabled() && groupMsgVO.isPub()) {
                groupMsgVO.setDisabled(true);
            }
            // 私聊群头像和群名换成对方的。私聊群不显示人数
            if (!groupMsgVO.isPub()) {
                // 查询对方用户昵称、头像
                GroupMember groupMember = groupMemberService.fetch(Cnd.where("groupId", "=", groupMsgVO.getId())
                        .and("userId", "!=", userId));
                if (groupMember != null) {
                    User user = userService.fetch(Cnd.where("id", "=", groupMember.getUserId()));
                    // 组装私聊群头像、昵称
                    groupMsgVO.setGroupName(user.getNickName());
                    groupMsgVO.setGroupHeadPicture(user.getHeadPicture());
                }
            } else {
                // 公开群显示人数：真人数 + 假人数
                int fakeMemberNum = null != groupMsgVO.getMemberNum() ?  groupMsgVO.getMemberNum() : 0;
                int realMemberNum = groupMemberService.count(Cnd.where("groupId", "=", groupMsgVO.getId()));
                groupMsgVO.setMemberNum(fakeMemberNum + realMemberNum);
            }
            // 查询各个群的最后一条消息记录
            GroupLog groupLog = groupLogService.fetch(Cnd.where("groupId", "=", groupMsgVO.getId()).orderBy("ctAt", "desc"));
            if (groupLog != null) {
                User user = userService.fetch(groupLog.getUserId());
                groupMsgVO.setMsgId(groupLog.getId());
                groupMsgVO.setMsgSenderId(groupLog.getUserId());
                String redisKey = ChatConstant.UG + ":" + userId + ":" + groupMsgVO.getId();
                String flag = redisService.get(redisKey);
                if (Strings.isBlank(flag)) {
                    redisService.set(redisKey, "false");
                    flag = "false";
                }
                groupMsgVO.setReadFlag(flag);
                groupMsgVO.setMsgOprType(groupLog.getOprType());
                groupMsgVO.setMsgType(groupLog.getMsgType());
                groupMsgVO.setMsgContent(groupLog.getContent());
                groupMsgVO.setSendTime(groupLog.getCtAt());
                groupMsgVO.setNickname(user.getNickName());
                groupMsgVO.setUsername(user.getUserName());
            }
        }
        pagination.setList(list);
        return pagination;
    }

    @Override
    public Pagination listPage(int pageNumber, int pageSize, String searchKeyword, Boolean isOpen, Boolean isPub) {
        Sql sql = Sqls.create("SELECT i.*,u.nickname FROM group_info i ");
        Sql countSql = Sqls.create("SELECT count(1) FROM group_info i ");
        Sql joinSql = Sqls.create("LEFT JOIN user u ON u.id = i.masterId " +
                "$condition ");
        Cnd cnd = Cnd.where("i.delFlag", "=", 0);
        if (Strings.isNotBlank(searchKeyword)) {
            searchKeyword = "%" + searchKeyword + "%";
            cnd.and(Cnd.exps("i.groupName", "like", searchKeyword)
                    .or("u.nickName", "like", searchKeyword)
                    .or("u.email", "like", searchKeyword)
                    .or("u.userName", "like", searchKeyword));
        }
        if (null != isOpen) {
            cnd.and("i.open", "=", isOpen);
        }
        if (null != isPub) {
            cnd.and("i.pub", "=", isPub);
        }
        joinSql.setCondition(cnd);
        sql.appendSourceSql(joinSql.toString());
        sql.appendSourceSql("order by ctAt desc");
        countSql.appendSourceSql(joinSql.toString());
        Pagination pagination = dbService.listPage(GroupVO.class, pageNumber, pageSize, sql.toString(), countSql.toString());
        List<GroupVO> list = pagination.getList(GroupVO.class);
        for (GroupVO groupVO : list) {
            int realMemberNum = groupMemberService.count(Cnd.where("groupId", "=", groupVO.getId()));
            groupVO.setRealMemberNum(realMemberNum);
        }
        return pagination;
    }


    @Override
    public Pagination memberList(int pageNumber, int pageSize, String groupId, String searchKeyword) {
        //查询已入群的用户id
        Sql groupMemberSql = Sqls.create("SELECT userId FROM group_member WHERE groupId = @groupId AND delFlag = 0");
        groupMemberSql.setParam("groupId", groupId);
        groupMemberSql.setCallback(Sqls.callback.strs());
        groupMemberService.execute(groupMemberSql);
        List<String> list = groupMemberSql.getList(String.class);
        Sql sql = Sqls.create("SELECT id, nickName, email, userName " +
                "FROM user " +
                "$condition ");
        Sql countSql = Sqls.create("SELECT COUNT(1) FROM user " +
                "$condition ");
        Cnd cnd = Cnd.where("id", "NOT IN ", list);
        if (Strings.isNotBlank(searchKeyword)) {
            cnd.and(Cnd.exps("nickName", "=", searchKeyword)
                    .or("userName", "=", searchKeyword));
        }
        sql.setCondition(cnd);
        sql.appendSourceSql("ORDER BY lastLoginAt DESC");
        countSql.setCondition(cnd);
        return userService.listPage(pageNumber, pageSize, sql, countSql);
    }


    @Aop(TransAop.READ_COMMITTED)
    @Override
    public void edit(GroupInfo groupInfo, String opBy) {
        GroupInfo oldGroupInfo = this.fetch(groupInfo.getId());
        //当更换群主时，删除原群主，新群主若不再群里则添加新成员
        if (!oldGroupInfo.getMasterId().equals(groupInfo.getMasterId())) {
            GroupMember groupMember = groupMemberService.fetch(Cnd.where("userId", "=", groupInfo.getMasterId())
                    .and("groupId", "=", groupInfo.getId()));
            if (groupMember == null) {
                groupMember = new GroupMember();
                groupMember.setUserId(groupInfo.getMasterId());
                groupMember.setGroupId(groupInfo.getId());
                groupMember.setSort(0);
                groupMember.setManager(2);
                groupMemberService.insert(groupMember);
            } else {
                groupMember.setSort(0);
                groupMember.setDelFlag(false);
                groupMember.setManager(2);
                groupMemberService.update(groupMember);
            }
            //添加消息记录
            String content = "管理员更换了群主，现群主 " + userService.fetch(groupMember.getUserId()).getNickName();
            groupLogService.groupSend(oldGroupInfo.getMasterId(), oldGroupInfo.getId(), content, MsgTypeEnum.TEXT, OprTypeEnum.REMOVED, true);
            //移除原群主(先发消息，才能删)
            groupMemberService.delMember(oldGroupInfo.getMasterId(), oldGroupInfo.getId());
        }

        this.update(Chain.make("groupName", groupInfo.getGroupName())
                .add("groupHeadPicture", groupInfo.getGroupHeadPicture())
                .add("masterId", groupInfo.getMasterId())
                .add("memberNum", groupInfo.getMemberNum())
                .add("opAt", Times.getTS())
                .add("disabled", groupInfo.isDisabled())
                .add("open", groupInfo.isOpen())
                .add("opAt", Times.getTS())
                .add("opBy", opBy), Cnd.where("id", "=", groupInfo.getId()));
    }


    @Aop(TransAop.READ_COMMITTED)
    @Override
    public void add(GroupInfo groupInfo) {
        groupInfo.setPub(true);
        groupInfo = this.insert(groupInfo);
        //添加群主
        GroupMember groupMember = new GroupMember();
        groupMember.setUserId(groupInfo.getMasterId());
        groupMember.setGroupId(groupInfo.getId());
        groupMember.setManager(2);
        groupMember.setSort(0);
        groupMemberService.insert(groupMember);

    }

    @Aop(TransAop.READ_COMMITTED)
    @Override
    public void delGroup(String id) {
        // 删群
        this.delete(id);
        // 删除群群成员
        groupMemberService.delMember(id);
        // 删除所有聊天记录
        groupLogService.delLog(id);

    }

    @Override
    public Pagination applyList(String appid, int pageNumber, int pageSize) {
        Sql sql = Sqls.create("SELECT *" +
                " FROM group_info " +
                " WHERE id NOT IN " +
                " (SELECT i.id FROM group_member m LEFT JOIN group_info i ON i.id = m.groupId WHERE m.userId = @userId" +
                " AND m.delFlag = 0 AND i.delFlag = 0)" +
                " AND delFlag = 0 AND open = 1");
        sql.setParam("userId", appid);

        Pagination pagination = dbService.listPage(GroupMsgVO.class, pageNumber, pageSize, sql.toString());
        List<GroupMsgVO> list = pagination.getList(GroupMsgVO.class);
        // 群人数
        for (GroupMsgVO groupMsgVO : list) {
            // 假人数
            int fakeMemberNum = null != groupMsgVO.getMemberNum() ?  groupMsgVO.getMemberNum() : 0;
            int realMemberNum = groupMemberService.count(Cnd.where("groupId", "=", groupMsgVO.getId()));
            // 显示人数改成真人数加假人数
            groupMsgVO.setMemberNum(fakeMemberNum + realMemberNum);
        }
        return pagination;
    }

    @Override
    @Aop(TransAop.READ_COMMITTED)
    public GroupInfo privateChat(String appid, String userId, String masterId) {
        User master = userService.fetch(appid);
        User user = userService.fetch(userId);
        if (null == master || null == user) {
            Result.breakWith(BizExceptionEnum.USER_NOT_EXIST);
        }
        // 群名用双方昵称组成。给前端返回时，群名要set成对方的昵称。
        String groupName = "'" + master.getUserName() + "'&'" + user.getUserName() + "' 1v1";
        GroupInfo groupInfo = new GroupInfo();
        groupInfo.setGroupName(groupName);
        groupInfo.setOpen(false);
        groupInfo.setPub(false);
        // 群主id固定。不用userId，防止1v1私聊群拉人，前端根据此固定id区分群红包和个人红包。
        groupInfo.setMasterId(masterId);
        groupInfo = this.insert(groupInfo);
        // 添加群主
        GroupMember groupMember = new GroupMember();
        groupMember.setUserId(master.getId());
        groupMember.setGroupId(groupInfo.getId());
        groupMember.setSort(0);
        groupMemberService.insert(groupMember);
        // 添加被私聊人
        groupMember.setUserId(user.getId());
        groupMember.setGroupId(groupInfo.getId());
        groupMember.setSort(1);
        groupMemberService.insert(groupMember);
        // 创建成功返回给前端，需要处理头像、群名
        groupInfo.setGroupName(user.getNickName());
        groupInfo.setGroupHeadPicture(user.getHeadPicture());
        return groupInfo;
    }


    @Override
    @Aop(TransAop.READ_COMMITTED)
    public GroupInfo onPrivateChat(String appid, String userId) {
        User user = userService.fetch(userId);
        if (null == user) {
            Result.breakWith(BizExceptionEnum.USER_NOT_EXIST);
        }
        Sql sql = Sqls.create("SELECT gi.* FROM group_info as gi")
                .appendSourceSql("LEFT JOIN group_member AS gm ON gi.id = gm.groupId")
                .appendSourceSql("WHERE gm.userId = @appid AND gi.pub = 0")
                .appendSourceSql("AND gi.id in")
                .appendSourceSql("(SELECT gi.id FROM group_info AS gi LEFT JOIN group_member AS gm ON gi.id = gm.groupId")
                .appendSourceSql("WHERE gm.userId = @userId and gi.pub = 0)")
                .setParam("appid", appid)
                .setParam("userId", userId);
        GroupInfo groupInfo = dbService.fetchSql(GroupInfo.class, sql.toString());
        if (null != groupInfo) {
            groupInfo.setGroupName(user.getNickName());
            groupInfo.setGroupHeadPicture(user.getHeadPicture());
        }
        return groupInfo;
    }

    @Override
    @Aop(TransAop.READ_COMMITTED)
    public GroupInfo onlineAttendant(String userId) {
        // 如果已存在客服与该用户的私聊群，则直接返回，不再创建新的
        GroupInfo groupInfo = this.onAttendantChat(userId);
        if (null != groupInfo) {
            return groupInfo;
        }
        // 不存在私聊群则创建群,并返回群信息
        List<User> attendantList = userService.query(Cnd.where("attendant", "=", 1)
                .and("isOnline", "=", 1));
        User attendant;
        // 没有在线客服，分配最近上线的一个
        if (attendantList.isEmpty()) {
            attendant = userService.fetch(Cnd.where("attendant", "=", 1)
                    .orderBy("lastLoginAt", "desc"));
        } else {
            // 随机分配一个在线的客服
            attendant = attendantList.get(new Random().nextInt(attendantList.size()));
        }
        groupInfo = this.privateChat(userId, attendant.getId(), ChatConstant.ATT_PRI_CHAT);
        // 客服欢迎语
        groupLogService.groupSend(attendant.getId(),
                groupInfo.getId(),
                "Welcome to CS service, what can I do for you today?",
                MsgTypeEnum.TEXT,
                OprTypeEnum.SENT, false);

        return groupInfo;
    }

    @Override
    @Aop(TransAop.READ_COMMITTED)
    public GroupInfo onAttendantChat(String userId) {
        Sql sql = Sqls.create("SELECT gi.* FROM group_info as gi")
                .appendSourceSql("LEFT JOIN group_member AS gm ON gi.id = gm.groupId")
                .appendSourceSql("WHERE gm.userId = @userId AND gi.pub = 0")
                .appendSourceSql("AND gi.masterId = @masterId")
                .setParam("userId", userId)
                .setParam("masterId", ChatConstant.ATT_PRI_CHAT);
        GroupInfo groupInfo = dbService.fetchSql(GroupInfo.class, sql.toString());
        // 已存在与客服的私聊群，群头像设置成客服头像，群名设置成客服的昵称
        if (null != groupInfo) {
            GroupMember groupMember = groupMemberService.fetch(Cnd.where("groupId", "=", groupInfo.getId())
                    .and("userId", "!=", userId));
            User attendant = userService.fetch(groupMember.getUserId());
            groupInfo.setGroupName(attendant.getNickName());
            groupInfo.setGroupHeadPicture(attendant.getHeadPicture());
        }
        return groupInfo;
    }

    @Override
    @Aop(TransAop.READ_COMMITTED)
    public void transferAttendant(String groupId, String appid, String attendantId) {
        // 当前客服
        User currentAttendant = userService.fetch(appid);
        if (!currentAttendant.isAttendant()) {
            Result.breakWith(BizExceptionEnum.NOT_ATTENDANT);
        }
        // 要移交的客服
        User anotherAttendant = userService.fetch(attendantId);
        if (!anotherAttendant.isAttendant()) {
            Result.breakWith(BizExceptionEnum.NOT_ATTENDANT);
        }
        // 更新群成员
        Cnd cnd = Cnd.where("groupId", "=", groupId).and("userId", "=", appid);
        groupMemberService.update(Chain.make("userId", attendantId), cnd);
        // 群名字修改
        GroupInfo group = this.fetch(groupId);
        String groupName = group.getGroupName();
        groupName = groupName.replace(currentAttendant.getUserName(), anotherAttendant.getUserName());
        group.setGroupName(groupName);
        this.update(group);
    }
}
